require "rubygems"
require "net/http"
require 'uri'
require "hpricot"
require "open-uri"
require "digest/md5"
require 'fileutils'
require File.join(File.dirname(__FILE__), 'file_cache')

include FileUtils::Verbose

class FlickrApi

  def initialize(size="Original")
    @api_key = "f275dbd99274fc8b409d3589361e5ffe"
    @secret = "38154c2b169e728d"
    @frob_cache = FileCache.new(File.join(File.dirname(__FILE__), "frob_cache"))
    @token_cache = FileCache.new(File.join(File.dirname(__FILE__), "token_cache"))                                
    @size = size
  end

  def authorize
    unless has_token?
      retrieve_frob 
      authorize_application
      retrieve_token
    end
  end

  def download_photoset(photoset_id, destination_path)
    puts "downloading photoset with id => #{photoset_id}"

    create_destination_path(destination_path)
    photos = get_photos(photoset_id)

    puts photos

    (photos/"rsp/photoset/photo").each do |photo|
      save_photo(photo, destination_path)
    end

    puts "photoset => #{photoset_id} download to => #{destination_path} successfully :D"
  end

  private

  def save_photo(photo, destination_path)
    photo_id = photo["id"]
    photo_url = get_size_url(photo_id)

    photo_name = photo_url[photo_url.rindex("/")+1, 1000]
    destination_file = File.join(destination_path, photo_name)

    if File.exists?(destination_file)
      puts "photograph => #{photo_name} already downloaded, skipping..."
    else
      open(destination_file, "w").write(open(photo_url).read)
      $stdout.write(".")
      $stdout.flush
    end
  end

  def get_photos(photoset_id)
    url = build_url("rest", 
                    :method => "flickr.photosets.getPhotos", 
                    :photoset_id => photoset_id,
                    :auth_token => @token_cache.get)
    doc = Hpricot(open(url))
    return doc
  end

  def get_size_url(photo_id)
    url = build_url("rest", 
                    :method => "flickr.photos.getSizes", 
                    :photo_id => photo_id, 
                    :auth_token => @token_cache.get)
    doc = Hpricot(open(url))
    return doc.at("rsp/sizes/size[@label='#{@size}']")["source"]
  end

  def create_destination_path(destination_path)
    unless File.exists?(destination_path)
      puts "#{destination_path} does not exist, creating"
      mkdir(destination_path)   
    end
  end

  def has_token?
    return @token_cache.has_value?
  end

  def retrieve_frob
    unless @frob_cache.has_value?
      url = build_url("rest", :method => "flickr.auth.getFrob")
      doc = Hpricot(open(url))
      frob = (doc/"rsp/frob").inner_html
      @frob_cache.set(frob)
    end
  end

  def authorize_application
    auth_url = build_url("auth", :perms => "read", :frob => @frob_cache.get) 
    `open "#{auth_url}"`
    
    puts "authorize the application, then press any key to continue..."
    gets
  end

  def retrieve_token
    unless @token_cache.has_value?
      url = build_url("rest", :method => "flickr.auth.getToken", :frob => @frob_cache.get)
      doc = Hpricot(open(url))
      token = (doc/"auth/token").inner_html
      @token_cache.set(token) 
    end
  end

  def build_url(service, opts={})
    merged = { 
      :api_key => @api_key
    }.merge(opts)

    sorted = merged.sort { |x,y| x[0].to_s <=> y[0].to_s }
    api_sig = Digest::MD5.hexdigest(@secret + sorted.inject("") { |acc, h| acc << h[0].to_s << h[1].to_s })
    merged[:api_sig] = api_sig

    params = merged.inject("") { |acc, h| acc << "#{h[0]}=#{h[1]}&" }
    url = "http://api.flickr.com/services/#{service}/?#{params}"

    puts url
    return url
  end


end 
